//
// Created by user on 20-5-5.
//

#include "HEPT.h"
#include "../../InterruptManagement/APIC.h"

hwInterruptController HPETInterruptController =
        {
                .enable = IOAPICEnable,
                .disable = IOAPICDisable,
                .install = IOAPICInstall,
                .uninstall = IOAPICUninstall,
                .ack = IOAPICEdgeAck,
        };

extern Timer timerListHead;

void HPETInit() {

    unsigned int x;
    unsigned int *p = NULL;

    // 0xfed00000 是HPET寄存器映射地址首地址
    unsigned char *HPETAddress = (unsigned char *) Physical_To_Virtual(0xfed00000);
    IOAPICRETEntry entry;

    // 1. 首先获取到 RCBA 寄存器的地址
    io_out32(0xcf8, 0x8000f8f0);
    x = io_in32(0xcfc);
    x = x & 0xffffc000;

    // 2. 然后通过获取的地址加上 0x3404UL 偏移得到 HPTC寄存器的地址
    if (x > 0xfec00000 && x < 0xfee00000) {
        p = (unsigned int *) Physical_To_Virtual(x + 0x3404UL);
    }

    // 3. 开启 HPET
    *p = 0x80;
    mfence();

    // 4. 配置HPET的处理函数的中断处理描述符
    entry.vector = 34;
    entry.deliverMode = APIC_ICR_IOAPIC_Fixed;
    entry.destMode = ICR_IOAPIC_DELV_PHYSICAL;
    entry.deliver_status = APIC_ICR_IOAPIC_Idle;
    entry.polarity = APIC_IOAPIC_POLARITY_HIGH;
    entry.irr = APIC_IOAPIC_IRR_RESET;
    entry.trigger = APIC_ICR_IOAPIC_Edge;
    entry.mask = APIC_ICR_IOAPIC_Masked;
    entry.reserved = 0;

    entry.destination.physical.reserved1 = 0;
    entry.destination.physical.phy_dest = 0;
    entry.destination.physical.reserved2 = 0;

    // 5. 注册IRQ 中断处理函数
    registerIRQ(34, &entry, &HPETHandler, NULL, &HPETInterruptController, "HPET");

    printfColor(RED, BLACK, "HPET - GCAP_ID:<%lx>\n", *(unsigned long *) HPETAddress);

    // 6. 设置整体配置寄存器，置位0位和1位
    // 置位1位是为了指示定时器0可以向8259A的IRQ0引脚或I/O APIC的IRQ2引脚发送中断请求
    // 置位0位是为了让HPET定时器产生中断
    *(unsigned long *) (HPETAddress + 0x10) = 3;
    mfence();

    // 7. 设置定时器0的配置寄存器，置位6位、3位和2位
    // 置位6位是指示软件可以在定时器运行是直接修改定时值
    // 置位3位是表示定时器周期性产生中断
    // 置位2位是表示定时器可以中断
    *(unsigned long *) (HPETAddress + 0x100) = 0x004c;
    mfence();

    // 8. 设置定时器0的对比寄存器
    *(unsigned long *) (HPETAddress + 0x108) = 14318179;
    mfence();

    // 9. 获取 CMOS 时间
    getCmosTime(&time);

    // 10. 设置主计数器寄存器为0
    *(unsigned long *) (HPETAddress + 0xf0) = 0;
    mfence();

    printfColor(RED, BLACK, "year: %lx, month: %lx, day: %lx, hour: %lx, mintue: %lx, second: %lx\n", time.year,
                time.month, time.day, time.hour, time.minute, time.second);
}

void HPETHandler(unsigned long nr, unsigned long parameter, PerformSite *regs) {

    struct INT_CMD_REG icrEntry;

    // 1. 时间计数器加1
    jiffies++;

//    printfColor(RED, BLACK, "HPET--%d\n", jiffies);

    memset(&icrEntry, 0, sizeof(struct INT_CMD_REG));

    // 向3号处理器发送中断信号
    icrEntry.vector = 0xc8;
    icrEntry.destShorthand = ICR_No_Shorthand;
    icrEntry.trigger = APIC_ICR_IOAPIC_Edge;
    icrEntry.destMode = ICR_IOAPIC_DELV_PHYSICAL;
    icrEntry.destination.x2apicDestination = 3;
    icrEntry.deliverMode = APIC_ICR_IOAPIC_Fixed;
    wrmsr(0x830, *(unsigned long *) &icrEntry);

    // 2. 触发软中断
    if ((containerOf(listNext(&timerListHead.list), Timer, list)->expireJiffies <= jiffies)) {
        printfColor(INDIGO, BLACK, "ruan zhong duan!\n");
        setSoftIRQStatus(TIMER_SIRQ);
    }

    // 3. 根据当前进程的优先级来更新它的运行时间和剩下的可执行时间
    switch (current->priority) {
        case 0:
        case 1:
            taskSchedule[SMPCPUID()].CPUExecTaskJiffies--;
            current->vrunTime += 1;
            break;
        case 2:
        default:
            taskSchedule[SMPCPUID()].CPUExecTaskJiffies -= 2;
            current->vrunTime += 2;
            break;
    }

    // 4. 如果当前进程可执行时间归0，就置位进程的可调度位
    if(taskSchedule[SMPCPUID()].CPUExecTaskJiffies <= 0)
        current->flags |= NEED_SCHEDULE;

//    printfColor(INDIGO, BLACK, "HEPT End!\n");
}